package api;

import api.connection.DBConnection;
import api.result.Result;
import api.result.ResultKV;
import api.result.ResultList;

import java.util.ArrayList;
import java.util.List;

/***
 * MySQL: 吴小波
 * Mongo: 徐涵煜、刘俊坚
 * Redis: 杨觉旭、王燕飞
 * BenchmarkModule是一个抽象类，根据输入config的不同实例化成不同的Benchmark，
 * 比如，测试MongoDB上的TPC-H时就会实例化成TPCCBenchMongo。
 * 使用方法为通过BenchmarkFactory产生相对应的BenchmarkModule，然后调用loadData()
 * 开始转载数据，最终执行runBenchmark()运行测试
 */
public abstract class BenchmarkModule {
    public final static String MongoName = "mongo";
    public final static String MySqlName = "mysql";
    public final static String RedisName = "redis";

    protected String databaseName;
    protected String benchmarkName;
    protected Configuration config;
    protected List<Terminal> terminals;

    /***
     * @param config 测试参数
     */
    public BenchmarkModule(Configuration config) {
        this.config = config;
        this.terminals = new ArrayList<Terminal>();
    }

    /***
     * 根据不同的数据创建相对应的Connection，每个数据库Driver都有类似Connection或
     * Client的东西，通过这个Connection或Client来执行不同的查询。
     * 这里的DBConnection只是个接口，具体实现根据不同的数据自己实现，
     * 比如MongoDB则需要在api/connection/ConnectionMongo中继承相应的Connection或Client，
     * 并重载所需要的方法。
     * @return 以mongo为例，新建并返回一个api.connection.ConnectionMongo
     */
    public abstract DBConnection makeConnection();

    /***
     * 根据不同的参数实例化相应的用来装载数据的Loader
     * @return 实例化的Loader
     */
    public abstract Loader makeLoaderImpl();

    /***
     * 创建一个虚拟的Terminal
     * @param id terminal的id，按顺序传入
     * @return TPCCTerminal或TPCHTerminal
     */
    public abstract Terminal makeTerminalImpl(int id);

    /***
     * 装载数据
     * @return
     */
    public Result loadData() {
        if (config.getLoad_data()) {
            Loader loader = makeLoaderImpl();
            return loader.loadData(makeConnection());
        } else {
//            return new ResultKV<String>("loadData", "None");
            return null;
        }

    }

    /***
     * 启动各个terminal，运行测试计划
     * @return
     */
    public Result runBenchmark() {
        int numTerminal = config.getTerminals();
        Thread[] threads = new Thread[numTerminal];

        for (int i = 0; i < numTerminal; i++) {
            Terminal terminal = makeTerminalImpl(i);
            terminals.add(terminal);
            threads[i] = new Thread(terminal);
        }

        for (Thread thread : threads) {
            thread.start();
        }

        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        Result results = null;
        for (Terminal terminal : terminals) {
            results = terminal.getResults().union(results);
        }
        return results;
    }

    public String getDatabaseName() {
        return databaseName;
    }

    public String getBenchmarkName() {
        return benchmarkName;
    }
}
